package com.atguigu.tingshu.common.aspect;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.anno.GuiguCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
@Slf4j
public class GuiguCacheAspect {

    @Autowired
    private RedisTemplate<String , String> redisTemplate ;

    @Autowired
    private RedissonClient redissonClient ;

    @SneakyThrows
    @Around(value = "@annotation(guiguCache)")
    public Object around(ProceedingJoinPoint joinPoint , GuiguCache guiguCache) {

        // 从缓存中查询数据
        String cacheKeyExpression = guiguCache.cacheKey();          // album:info:#{#params[0]}
        String cacheKey = this.expressionParse(joinPoint, cacheKeyExpression, String.class);
        String json = redisTemplate.opsForValue().get(cacheKey);

        // 获取目标方法的返回值类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();          // 获取目标方法
        Type genericReturnType = method.getGenericReturnType();

        if(!StringUtils.isEmpty(json)) {
            Object object = JSON.parseObject(json, genericReturnType);
            log.info("从缓存中查询专辑数据...");
            return object ;
        }

        // 获取guiguCache和分布式锁相关的属性值
        boolean enableLock = guiguCache.enableLock();
        String lockNameExpression = guiguCache.lockName();

        if(enableLock && !StringUtils.isEmpty(lockNameExpression)) {        // 说明当前的这个接口需要使用分布式锁

            // 使用分布式锁解决缓存击穿的问题
            String lockName = this.expressionParse(joinPoint, lockNameExpression, String.class);
            RLock rLock = redissonClient.getLock(lockName);
            boolean tryLock = rLock.tryLock();
            if(tryLock) {			// 获取到锁，从数据库中查询数据

                log.info(Thread.currentThread().getId() + "该线程获取到了分布式锁，查询数据...");

                try {

                    // 从数据库中查询数据
                    Object result = joinPoint.proceed();
                    redisTemplate.opsForValue().set(cacheKey , JSON.toJSONString(result) , RedisConstant.ALBUM_TIMEOUT , TimeUnit.DAYS);
                    log.info("从从数据库中查询到了数据...");

                    // 返回查询到的数据
                    return result ;

                }catch (Throwable e) {
                    e.printStackTrace();
                    throw new GuiguException(ResultCodeEnum.DATA_ERROR) ;
                } finally {
                    log.info(Thread.currentThread().getId() + "该线程释放了分布式锁...");
                    rLock.unlock();
                }


            }else {

                log.info(Thread.currentThread().getId() + "该线程暂时没有从Redis中查询到数据，进行自旋....");
                return this.around(joinPoint , guiguCache) ;		// 进行自旋

            }


        }else {         // 不需要使用分布式锁

            // 从数据库中查询数据
            Object result = joinPoint.proceed();
            redisTemplate.opsForValue().set(cacheKey , JSON.toJSONString(result) , RedisConstant.ALBUM_TIMEOUT , TimeUnit.DAYS);
            log.info("从从数据库中查询到了数据...");

            // 返回查询到的数据
            return result ;

        }
    }

    // 创建一个表达式解析器对象
    private SpelExpressionParser parser = new SpelExpressionParser() ;

    // 对表达式进行解析
    public <T> T expressionParse(ProceedingJoinPoint proceedingJoinPoint , String expressionStr , Class<T> clazz) {

        //  调用SpelExpressionParser中parseExpression方法对表达式进行解析，到表达式对象
        Expression expression = parser.parseExpression(expressionStr, ParserContext.TEMPLATE_EXPRESSION);

        // 创建EvaluationContext对象，封装预设的数据
        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
        evaluationContext.setVariable("params" , proceedingJoinPoint.getArgs());

        // 获取解析结果
        T value = expression.getValue(evaluationContext, clazz);

        // 返回
        return value ;

    }

}
